package me.tonywang.config;

import me.tonywang.quartz.SchedulerCluserFactoryBean;
import me.tonywang.quartz.dao.CrontaskMapper;
import me.tonywang.quartz.entity.Crontask;
import org.quartz.*;
import org.quartz.spi.TriggerFiredBundle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.quartz.QuartzProperties;
import org.springframework.boot.autoconfigure.quartz.SchedulerFactoryBeanCustomizer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;
import org.springframework.util.Assert;

import javax.sql.DataSource;
import java.util.List;
import java.util.Map;
import java.util.Properties;


@Configuration
public class QuartzConfig {

    private final static String APPLICATION_CONTEXT_SCHEDULER_CONTEXT_KEY = "applicationContext";


    private final QuartzProperties properties;

    private final List<SchedulerFactoryBeanCustomizer> customizers;


    private final ApplicationContext applicationContext;

    public QuartzConfig(QuartzProperties properties, List<SchedulerFactoryBeanCustomizer> customizers, ApplicationContext applicationContext) {
        this.properties = properties;
        this.customizers = customizers;
        this.applicationContext = applicationContext;
    }

    @Bean
    @ConditionalOnMissingBean
    public SchedulerFactoryBean quartzScheduler(DataSource  dataSource) {
        SchedulerCluserFactoryBean schedulerFactoryBean = new SchedulerCluserFactoryBean(dataSource);
        schedulerFactoryBean.setDisable(true);
        schedulerFactoryBean.setJobFactory( new AutowireCapableBeanJobFactory(
				this.applicationContext.getAutowireCapableBeanFactory()));
        if (!this.properties.getProperties().isEmpty()) {
            schedulerFactoryBean
                    .setQuartzProperties(asProperties(this.properties.getProperties()));
        }
        customize(schedulerFactoryBean);
        return schedulerFactoryBean;
    }


    private Properties asProperties(Map<String, String> source) {
        Properties properties = new Properties();
        properties.putAll(source);
        return properties;
    }

    private void customize(SchedulerFactoryBean schedulerFactoryBean) {
        if (this.customizers != null) {
            for (SchedulerFactoryBeanCustomizer customizer : this.customizers) {
                customizer.customize(schedulerFactoryBean);
            }
        }
    }

    class AutowireCapableBeanJobFactory extends SpringBeanJobFactory {

	private final AutowireCapableBeanFactory beanFactory;

	AutowireCapableBeanJobFactory(AutowireCapableBeanFactory beanFactory) {
		Assert.notNull(beanFactory, "Bean factory must not be null");
		this.beanFactory = beanFactory;
	}

	@Override
	protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
		Object jobInstance = super.createJobInstance(bundle);
		this.beanFactory.autowireBean(jobInstance);
		this.beanFactory.initializeBean(jobInstance, null);
		return jobInstance;
	}

}


    @Bean
    SchedulerFactoryBeanCustomizer schedulerFactoryBeanCustomizer(ObjectProvider<SchedulerListener[]> schedulerListeners,
                                                                  ObjectProvider<JobListener[]> jobListeners,
                                                                  ObjectProvider<TriggerListener[]> triggerListeners) {
        return (schedulerFactoryBean) -> {
            schedulerFactoryBean.setApplicationContextSchedulerContextKey(APPLICATION_CONTEXT_SCHEDULER_CONTEXT_KEY);
            schedulerFactoryBean.setSchedulerListeners(schedulerListeners.getIfAvailable());
            schedulerFactoryBean.setGlobalJobListeners(jobListeners.getIfAvailable());
            schedulerFactoryBean.setGlobalTriggerListeners(triggerListeners.getIfAvailable());
        };
    }


    @ConditionalOnProperty(prefix = "spring.quartz.jobListener", name = "enabled",
            havingValue = "true", matchIfMissing = false)
    @Bean
    JobListener jobListener() {


        return new JobListener() {

            @Autowired
            CrontaskMapper crontaskMapper;

            Logger logger = LoggerFactory.getLogger(this.getClass());

            @Override
            public String getName() {
                return this.getClass().getName();
            }

            @Override
            public void jobToBeExecuted(JobExecutionContext context) {
                JobKey jobKey = context.getJobDetail().getKey();
                Crontask crontask = new Crontask();
                crontask.setId(jobKey.toString());
//                crontask.setPreFireTime(context.getPreviousFireTime());
//                crontask.setPlanFireTime(context.getScheduledFireTime());
                crontask.setNextFireTime(context.getNextFireTime());
                crontask.setFireTime(context.getFireTime());
                crontask.setJobState("RUNNING");
                try {
                    crontask.setSchedName(context.getScheduler().getSchedulerName());
                    crontask.setSchedInst(context.getScheduler().getSchedulerInstanceId());
                } catch (SchedulerException e) {
//                    e.printStackTrace();
                }
                crontaskMapper.updateById(crontask);
            }

            @Override
            public void jobExecutionVetoed(JobExecutionContext context) {

            }

            @Override
            public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
                try {
                    logger.debug("run in schedulerName {}", context.getScheduler().getSchedulerName());
                } catch (SchedulerException e) {
                    logger.error(" error {} ", e);
                }
                logger.debug("jobKey : {}  ", context.getJobDetail().getKey());
                logger.debug("job FireInstanceId   : {}  ", context.getFireInstanceId());
                String targetBeanName = context.getJobDetail().getJobDataMap().getString("targetBeanName");
                String targetMethod = context.getJobDetail().getJobDataMap().getString("targetMethod");
                String requestObj = context.getJobDetail().getJobDataMap().getString("requestObj");
                logger.debug("jobDataMap targetBeanName: {}  ", targetBeanName);
                logger.debug("jobDataMap targetMethod: {}  ", targetMethod);
                logger.debug("jobDataMap requestObj: {}  ", requestObj);
                logger.debug("context result : {}  ", context.getResult());
                logger.debug("cost time : {}  ", context.getJobRunTime());

                logger.debug("======================================================================== ");
                logger.debug("job ScheduledFireTime   : {}  ", context.getScheduledFireTime());
                logger.debug("job fireTime : {}  ", context.getFireTime());
                logger.debug("job FireInstanceId   : {}  ", context.getFireInstanceId());
                logger.debug("job PreviousFireTime   : {}  ", context.getPreviousFireTime());
                logger.debug("job NextFireTime   : {}  ", context.getNextFireTime());


                //上次完成时间
                JobKey jobKey = context.getJobDetail().getKey();
                Crontask crontask = new Crontask();
                crontask.setId(jobKey.toString());
//                crontask.setPreFinshTime(new java.util.Date());
                crontaskMapper.updateById(crontask);

                if (jobException != null)
                    logger.error("jobException {} ", jobException.getMessage());
            }
        };
    }

    @Bean
    SchedulerListener schedulerListener() {
        return new SchedulerListener() {
            Logger logger = LoggerFactory.getLogger(this.getClass());

            @Override
            public void jobScheduled(Trigger trigger) {

            }

            @Override
            public void jobUnscheduled(TriggerKey triggerKey) {

            }

            @Override
            public void triggerFinalized(Trigger trigger) {

            }

            @Override
            public void triggerPaused(TriggerKey triggerKey) {
                logger.debug("triggerKey {} ", triggerKey);
            }

            @Override
            public void triggersPaused(String triggerGroup) {
                logger.debug(triggerGroup);
            }

            @Override
            public void triggerResumed(TriggerKey triggerKey) {

            }

            @Override
            public void triggersResumed(String triggerGroup) {

            }

            @Override
            public void jobAdded(JobDetail jobDetail) {
                logger.debug("jobAdded " + jobDetail.getKey());
            }

            @Override
            public void jobDeleted(JobKey jobKey) {

            }

            @Override
            public void jobPaused(JobKey jobKey) {
                logger.debug("jobPaused {} " + jobKey);
            }

            @Override
            public void jobsPaused(String jobGroup) {
                logger.debug("jobGroupPaused {} " + jobGroup);
            }

            @Override
            public void jobResumed(JobKey jobKey) {

            }

            @Override
            public void jobsResumed(String jobGroup) {

            }

            @Override
            public void schedulerError(String msg, SchedulerException cause) {

            }

            @Override
            public void schedulerInStandbyMode() {

            }

            @Override
            public void schedulerStarted() {

            }

            @Override
            public void schedulerStarting() {

            }

            @Override
            public void schedulerShutdown() {

            }

            @Override
            public void schedulerShuttingdown() {

            }

            @Override
            public void schedulingDataCleared() {

            }
        };
    }


}
